package main
{
	import com.bit101.components.Component;
	
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Vector3D;
	import flash.net.FileFilter;
	import flash.net.FileReference;
	import flash.ui.Keyboard;
	import flash.utils.ByteArray;
	
	import away3d.containers.ObjectContainer3D;
	import away3d.containers.View3D;
	import away3d.controllers.FollowController;
	import away3d.controllers.HoverController;
	import away3d.controllers.LookAtController;
	import away3d.core.base.Geometry;
	import away3d.core.managers.Mouse3DManager;
	import away3d.core.pick.PickingType;
	import away3d.debug.AwayStats;
	import away3d.entities.Mesh;
	import away3d.entities.SegmentSet;
	import away3d.events.MouseEvent3D;
	import away3d.materials.ColorMaterial;
	import away3d.materials.TextureMaterial;
	import away3d.primitives.CubeGeometry;
	import away3d.primitives.LineSegment;
	import away3d.primitives.PlaneGeometry;
	import away3d.primitives.SphereGeometry;
	import away3d.primitives.WireframePlane;
	import away3d.utils.Cast;
	
	import base.BasePlane;
	import base.RectPlane;
	import base.RoundPlane;
	import base.TrackPoint;
	
	import comman.AxisIndicator;
	import comman.StageMouseManager;
	
	import configs.GlobalConsts;
	import configs.MakerConfig;
	
	import events.PropertyEvent;
	
	import geo.SectorGeometry;
	
	import managers.EventManager;
	
	import simulate.SimulateSystem;
	
	import views.AlertPanel;
	/**
	 * 3D拼图主体
	 * @author Ado
	 */
	public class MakerMain extends Sprite
	{
		private var material:TextureMaterial;
		private var _view:View3D;
		private var controller:HoverController;
		/** 性能指示器 */
		private var stats:AwayStats;
		/** 坐标系标示 */
		private var axisIndicator:AxisIndicator;
		/** 舞台鼠标控制 */
		private var stageMouseManager:StageMouseManager;
		/**摄像头控制*/
		private var cameraLeft:Boolean;
		private var cameraRight:Boolean;
		private var cameraForward:Boolean;
		private var cameraBackward:Boolean;
		private var placeHolder:ObjectContainer3D;
		/**物体控制*/
		private var objLeft:Boolean;
		private var objRight:Boolean;
		private var objForward:Boolean;
		private var objBackward:Boolean;
		private var ctrKey:Boolean;
		/**移动的单位步数*/
		private var step:Number = 10;
		private var showAxis:Boolean;
		/** 赛道缝合用:主动粘合的赛道 */
		private var sndPlane:BasePlane;
		/** 赛道缝合用:被粘合的赛道 */
		private var frtPlane:BasePlane;
		/** 赛道数据相关 */
		private var planes:Vector.<BasePlane>;
		private var keypoints:Vector.<TrackPoint>;
		private var currentId:int = 0;
		/**读取和保存配置信息的文件参考*/
		private var file:FileReference;
		/**绘制线路相关*/
		private var drawing:Boolean;
		/** 当前选中的Mesh，用于delete */
		private var currentMesh:Mesh;
		/** 当前选中的路线点 */
		private var currentPoint:TrackPoint;
		/** 模拟弯道的时候显示的弯心 */
		private var centerPoint:Mesh;
		public function MakerMain()
		{
			if(stage)
				init();
			else 
				addEventListener(Event.ADDED_TO_STAGE, init);
		}
		private function init(e:Event=null):void
		{
			initEngine();//初始化引擎
			initMaterial();//初始化路面公用材质
			initAxis();//初始化坐标系指示
			initListenter();//初始化监听器
			planes = new Vector.<BasePlane>();
			keypoints = new Vector.<TrackPoint>();
			file = new FileReference();
		}
		private function initAxis():void
		{
			axisIndicator = new AxisIndicator(_view.scene);
			showAxisHandler();
		}
		private function initListenter():void
		{
			//界面通知监听
			EventManager.getInstance().addEventListener(PropertyEvent.ADD_ELEMENT, onAddElement);
			EventManager.getInstance().addEventListener(PropertyEvent.CHECK_NODES,checkNodes);
			EventManager.getInstance().addEventListener(PropertyEvent.SHOW_AXIS, showAxisHandler);
			EventManager.getInstance().addEventListener(PropertyEvent.SAVE_PLANE, savePlane);
			EventManager.getInstance().addEventListener(PropertyEvent.SAVE_PATH, savePath);
			EventManager.getInstance().addEventListener(PropertyEvent.COMBINE_PANEL, onCombine);
			EventManager.getInstance().addEventListener(PropertyEvent.IMPORT, onImport);
			EventManager.getInstance().addEventListener(PropertyEvent.DRAW_PATH, drawPath);
			EventManager.getInstance().addEventListener(PropertyEvent.SIMULATE, onSimulate);
			EventManager.getInstance().addEventListener(PropertyEvent.DRAW_CENTRE, onCenter);
			EventManager.getInstance().addEventListener(PropertyEvent.RESET_CAMERA, onResetCamera);
			
			//舞台事件，用于切换视角
			stageMouseManager = new StageMouseManager(stage);
			stageMouseManager.keyDownFunc = onKeyDown;
			stageMouseManager.keyUpFunc = onKeyUp;
			stageMouseManager.mouseDownFunc = mouseDown;
			stageMouseManager.mouseUpFunc = mouseUp;
			stage.addEventListener(MouseEvent.MOUSE_WHEEL, onWheel);
			
			this.addEventListener(Event.ENTER_FRAME, render);
		}
		private function onResetCamera(e:PropertyEvent):void
		{
			placeHolder.position = new Vector3D(0,0,0);
		}
		
		private function onCenter(e:PropertyEvent):void
		{
			if(centerPoint == null)
			{
				var sphere:SphereGeometry = new SphereGeometry(100);
				var material:ColorMaterial = new ColorMaterial(0xffff00);
				centerPoint = new Mesh(sphere,material);
				_view.scene.addChild(centerPoint);
			}
			var center:Vector3D = e.data as Vector3D;
			centerPoint.position = center;
		}
		private function drawPath(e:PropertyEvent):void
		{
			drawing = !drawing;
			for each(var p:TrackPoint in keypoints)
			{
				p.mouseChildren = p.mouseEnabled = drawing;
			}
		}
		private function onCombine(e:PropertyEvent):void
		{
			jointPlane(frtPlane,sndPlane,e.data.toString());
		}
		private function initMaterial():void
		{
			material = new TextureMaterial(Cast.bitmapTexture(GlobalConsts.RoadTile));
			material.repeat = true;
			MakerConfig.TRACK_MATERIAL = material;
		}
		private function initEngine():void
		{
			_view = new View3D();
			_view.antiAlias = 1;
			_view.backgroundColor = 0x999999;
			_view.backgroundAlpha = 0.6;
			_view.forceMouseMove = true;
			_view.width = stage.stageWidth;
			_view.height = stage.stageHeight;
			_view.mousePicker = PickingType.RAYCAST_BEST_HIT;
			_view.camera.lens.far = 10000;
			addChild(_view);
			
			placeHolder = new ObjectContainer3D();
			_view.scene.addChild(placeHolder);
			controller = new HoverController(_view.camera,placeHolder);
			
			stats = new AwayStats(_view);
			stats.y = stage.stageHeight - stats.height;
			addChild(stats);
		}
		private function onAddElement(e:PropertyEvent):void
		{
			var type:String = e.data.toString();
			if(type == MakerConfig.RECT_TYPE)
			{
				var mesh:BasePlane = new RectPlane(new PlaneGeometry(1000,MakerConfig.TRACK_WIDTH),material);
			}else if(type == MakerConfig.ROUND_TYPE)
			{
				mesh = new RoundPlane(new SectorGeometry(), material);
			}
			var pos:Vector3D = new Vector3D();
			pos.copyFrom(placeHolder.position);
			mesh.position = pos;
			initPlane(mesh);
		}
		private function initPlane(plane:BasePlane):void
		{
			plane.mouseEnabled = true;
			plane.countNode();
			plane.id = currentId;
			currentId++;
			planes.push(plane);
			initEvents(plane);
			_view.scene.addChild(plane);
		}
		private function initEvents(target:Mesh):void
		{
			target.addEventListener(MouseEvent3D.MOUSE_OVER, onMeshOver);
			target.addEventListener(MouseEvent3D.MOUSE_OUT, onMeshOut);
			target.addEventListener(MouseEvent3D.CLICK, onMeshClick);
		}
		/** 删除，回收Mesh */
		private function deleteTarget(mesh:Mesh):void
		{
			_view.scene.removeChild(mesh);
			if(mesh is BasePlane)
			{
				var index:int = planes.indexOf(mesh as BasePlane);
				planes.splice(index,1);
				EventManager.getInstance().dispatchEvent(new PropertyEvent(PropertyEvent.SELECT_ELEMENT,null));
			}else if(mesh is TrackPoint)
			{
				index = keypoints.indexOf(mesh as TrackPoint); 
				keypoints.splice(index , 1);
				EventManager.getInstance().dispatchEvent(new PropertyEvent(PropertyEvent.SELECT_POINT,null));
			}
			mesh.removeEventListener(MouseEvent3D.MOUSE_OVER, onMeshOver);
			mesh.removeEventListener(MouseEvent3D.MOUSE_OUT, onMeshOut);
			mesh.removeEventListener(MouseEvent3D.CLICK, onMeshClick);
			mesh.mouseEnabled = mesh.mouseChildren = false;
			mesh.dispose();
			mesh.disposeAsset();
		}
		private var currentPointId:int = 0;
		private function initPoints(point:TrackPoint):void
		{
			point.mouseEnabled = point.mouseChildren = drawing;
			point.id = currentPointId;
			if(keypoints.length == 0)
			{
				point.position = new Vector3D(0,0,0);
			}
			keypoints.push(point);
			_view.scene.addChild(point);
			initEvents(point);
			currentPointId++;
		}
		private function onMeshClick(e:MouseEvent3D):void
		{
			currentMesh = e.object as Mesh;
			if(drawing)
			{
				if(e.target is BasePlane)
				{
					var pos:Vector3D = e.scenePosition;
					var point:TrackPoint = new TrackPoint(new CubeGeometry());
					point.position = pos;
					point.rotationZ = e.object.rotationZ;
					initPoints(point);
				}else if(e.target is TrackPoint)
				{
					EventManager.getInstance().dispatchEvent(new PropertyEvent(PropertyEvent.SELECT_POINT, e.object));
					currentPoint = e.target as TrackPoint;
				}
			}else
			{
				if(sndPlane)
				{
					frtPlane = sndPlane;
				}
				sndPlane = BasePlane(e.object);
//				controller.lookAtObject = sndPlane;
				EventManager.getInstance().dispatchEvent(new PropertyEvent(PropertyEvent.SELECT_ELEMENT,e.object));
				if(sndPlane && frtPlane && sndPlane != frtPlane)
				{
					EventManager.getInstance().dispatchEvent(new PropertyEvent(PropertyEvent.SHOW_COMBINE_PANEL));
				}
			}
		}
		private function onMeshOver(e:MouseEvent3D):void
		{
			var mesh:Mesh = e.object as Mesh;
			mesh.showBounds = true;
		}
		private function onMeshOut(e:MouseEvent3D):void
		{
			var mesh:Mesh = e.object as Mesh;
			mesh.showBounds = false;
		}

		private function mouseDown(e:MouseEvent):void
		{
			if(e.target is  Component) return;
			if(sndPlane)
			{
				frtPlane = sndPlane;
			}
			sndPlane = null;
		}
		private function mouseUp(e:MouseEvent):void
		{
			controller.panAngle = (stageMouseManager.stageDeltaX >> 1) + controller.panAngle;
			controller.tiltAngle = (stageMouseManager.stageDeltaY >> 1) + controller.tiltAngle;
		}
		private function checkNodes(e:PropertyEvent=null):void
		{
			if(sndPlane == null) return;
			sndPlane.countNode();
			for each(var i:BasePlane in planes)
			{
				if(i == sndPlane)
				{
					continue;
				}
				if(Vector3D.distance(sndPlane.StartNode, i.StartNode) < 100)
				{
					jointPlane(i,sndPlane,"ss");
				}else if(Vector3D.distance(sndPlane.StartNode, i.EndNode) < 100)
				{
					jointPlane(i,sndPlane,"es");
				}else if(Vector3D.distance(sndPlane.EndNode, i.StartNode) < 100)
				{
					jointPlane(i,sndPlane,"se");
				}else if(Vector3D.distance(sndPlane.EndNode, i.EndNode) < 100)
				{
					jointPlane(i,sndPlane,"ee");
				}
			}
		}
		/**
		 * 粘和俩赛道 
		 * @param referPlane 不动的那个
		 * @param targetPlane 适应的那个
		 * @param type "es" 用来判断适应平面的那个点,第一位代表t，第二位代表s
		 * @return 
		 * 
		 */		
		private function jointPlane(referPlane:BasePlane, targetPlane:BasePlane, type:String):void
		{
			if(referPlane == null)
			{
				AlertPanel.getInstance().alert("参考平面为空");
				return;
			}else if(targetPlane == null)
			{
				AlertPanel.getInstance().alert("目标平面为空");
				return;
			}
			var targetNodeType:String = type.charAt(1);
			var referNodeType:String = type.charAt(0);
			if(referPlane is RectPlane)
			{
				if(targetPlane is RectPlane)
				{
					if(targetNodeType == referNodeType)
					{
						targetPlane.rotationY = referPlane.rotationY+180;
					}else
					{
						targetPlane.rotationY = referPlane.rotationY;
					}
				}else
				{
					if(targetNodeType == "s")
					{
						if(referNodeType == "s")
						{
							targetPlane.rotationY = referPlane.rotationY - 90;
						}else
						{
							targetPlane.rotationY = referPlane.rotationY + 90;
						}
					}else
					{
						if(referNodeType == "s")
						{
							targetPlane.rotationY = referPlane.rotationY  + 90 + SectorGeometry(targetPlane.geometry).angle;
						}else
						{
							targetPlane.rotationY = referPlane.rotationY - 90 + SectorGeometry(targetPlane.geometry).angle;
						}
					}
				}
			}else
			{
				var angle:Number = SectorGeometry(referPlane.geometry).angle;
				
				if(targetPlane is RectPlane)
				{
					if(targetNodeType ==  "s")
					{
						if(referNodeType == "s")
						{
							targetPlane.rotationY = referPlane.rotationY+90;
						}else
						{
							targetPlane.rotationY = referPlane.rotationY-90-angle;
						}
					}else
					{
						if(referNodeType == "s")
						{
							targetPlane.rotationY = referPlane.rotationY - 90;
						}else
						{
							targetPlane.rotationY = referPlane.rotationY - angle + 90;
						}
					}
				}else
				{
					var angle1:Number = SectorGeometry(targetPlane.geometry).angle;
					if(targetNodeType ==  "s")
					{
						if(referNodeType == "s")
						{
							targetPlane.rotationY = referPlane.rotationY + 180;
						}else
						{
							targetPlane.rotationY = referPlane.rotationY - angle;
						}
					}else
					{
						if(referNodeType == "s")
						{
							targetPlane.rotationY = referPlane.rotationY + angle1;
						}else
						{
							targetPlane.rotationY = referPlane.rotationY + 180 - angle + angle1;
						}
					}
				}
			}
			targetPlane.countNode();
			/** 适应面的适应点 */
			var targetPos:Vector3D =  referNodeType == "s" ? referPlane.StartNode : referPlane.EndNode;
			var srcPosition:Vector3D = targetNodeType == "s" ? targetPlane.StartNode : targetPlane.EndNode;
			/** X,Y,Z的差距 */
			var deltaX:Number = targetPos.x - srcPosition.x;
			var deltaY:Number = targetPos.y - srcPosition.y;
			var deltaZ:Number = targetPos.z - srcPosition.z;
			targetPlane.x += deltaX;
			targetPlane.y += deltaY;
			targetPlane.z += deltaZ;
			targetPlane.countNode();
		}
		private function onWheel(e:MouseEvent):void
		{
			controller.distance *= (1+e.delta/100); 
		}
		private function onKeyDown(e:KeyboardEvent):void
		{
			switch(e.keyCode)
			{
				case Keyboard.UP:
					objForward = true;
					break;
				case Keyboard.DOWN:
					objBackward = true;
					break;
				case Keyboard.LEFT:
					objLeft = true;
					break;
				case Keyboard.RIGHT:
					objRight = true;
					break;
				case Keyboard.CONTROL:
					ctrKey = true;
					break;
				case Keyboard.W:
					cameraForward = true;
					break;
				case Keyboard.A:
					cameraLeft = true;
					break;
				case Keyboard.S:
					cameraBackward = true;
					break;
				case Keyboard.D:
					cameraRight = true;
					break;
				default:
					break;
			}
		}
		private function onKeyUp(e:KeyboardEvent):void
		{
			switch(e.keyCode)
			{
				case Keyboard.UP:
					objForward = false;
					checkNodes();
					break;
				case Keyboard.DOWN:
					objBackward = false;
					checkNodes();
					break;
				case Keyboard.LEFT:
					objLeft = false;
					checkNodes();
					break;
				case Keyboard.RIGHT:
					objRight = false;
					checkNodes();
					break;
				case Keyboard.CONTROL:
					ctrKey = false;
					checkNodes();
					break;
				case Keyboard.W:
					cameraForward = false;
					break;
				case Keyboard.A:
					cameraLeft = false;
					break;
				case Keyboard.S:
					cameraBackward = false;
					break;
				case Keyboard.D:
					cameraRight = false;
					break;
				case Keyboard.DELETE:
					if(currentMesh)
						deleteTarget(currentMesh);
					break;
				default:
					break;
			}
		}
		private function render(e:Event):void
		{
			if(cameraLeft)
			{
				placeHolder.x -= step;
			}else if(cameraRight)
			{
				placeHolder.x += step;
			}else if(cameraForward)
			{
				placeHolder.z += step;
			}else if(cameraBackward)
			{
				placeHolder.z -= step;
			}else if(sndPlane || currentPoint)
			{
				var target:Mesh = drawing ? currentPoint : sndPlane;
				if(target == null) return;
				if(objLeft)
				{
					target.x -= step;
				}else if(objRight)
				{
					target.x += step;
				}else if(objForward)
				{
					if(ctrKey)
					{
						target.y += step;
					}else{
						target.z += step;
					}
					
				}else if(objBackward)
				{
					if(ctrKey)
					{
						target.y -= step;
					}else
					{
						target.z -= step;
					}
				}
			}
			_view.render();
		}
		private function showAxisHandler(e:PropertyEvent=null):void
		{
			showAxis = !showAxis;
			axisIndicator.visible = showAxis;
		}
		private function getPathConfig():XML
		{
			var xml:XML = XML("<root />");
			var info:XML = XML("<info />");
			info.@id = 0;
			info.@name = "shit";
			info.@content = "keypoints";
			info.@injection = planes[0].rotationY;
			xml.appendChild(info);
			var points:XML = XML("<keypoints />");
			var len:int = keypoints.length;
			for(var i:int=0; i<len; i++)
			{
				points.appendChild(keypoints[i].pathConfig());
			}
			
			xml.appendChild(points);
			return xml;
		}
		private function savePath(e:PropertyEvent):void
		{
			var xml:XML = getPathConfig();
			file.save(xml, "pathConfig.xml");
		}
		private function savePlane(e:PropertyEvent):void
		{
			var xml:XML = XML("<root />");
			var info:XML = XML("<info />");
			info.@id = 0;
			info.@name = "shit";
			info.@content = "meshes";
			xml.appendChild(info);
			var meshes:XML = XML("<meshes />");
			var len:int = planes.length;
			for(var i:int=0; i<len; i++)
			{
				meshes.appendChild(planes[i].planeXML());
			}
			xml.appendChild(meshes);
			file.save(xml,"nothing");
		}
		private function onImport(e:PropertyEvent):void
		{
			file.browse([new FileFilter("XML","*.xml")]);
			file.addEventListener(Event.SELECT, onSelected);
		}
		private function onSelected(e:Event):void
		{
			file.load();
			file.removeEventListener(Event.SELECT, onSelected);
			file.addEventListener(Event.COMPLETE, onComplete);
		}
		private function onComplete(e:Event):void
		{
			file.removeEventListener(Event.COMPLETE, onComplete);
			var data:ByteArray = new ByteArray();
			var str:String = file.data.readMultiByte(file.data.bytesAvailable,"utf-8");
			var rootNodeLabel:String = XML(str).child("info").@content;
			if(rootNodeLabel == "meshes")
			{
				for each(var b:BasePlane in planes)
				{
					deleteTarget(b);
				}
			}else if(rootNodeLabel == "keypoints")
			{
				for each(var j:TrackPoint in keypoints)
				{
					deleteTarget(j);
				}
			}else
			{
				AlertPanel.getInstance().alert("不是所需的文件...");
				return;
			}
			var meshes:XMLList = XML(str).child(rootNodeLabel)[0].children();
			for each(var i:* in meshes)
			{
				if(rootNodeLabel == "meshes")
				{
					var plane:BasePlane = BasePlane.createPlane(i);
					initPlane(plane);
				}else if(rootNodeLabel == "keypoints")
				{
					var point:TrackPoint = TrackPoint.createPoint(i);
					initPoints(point);
				}
			}
		}
		private function onSimulate(e:PropertyEvent):void
		{
			SimulateSystem.init(keypoints,planes[0].rotationY,planes[planes.length-1].rotationY);
		}
		public function onResize():void
		{
			_view.width = stage.stageWidth;
			_view.height = stage.stageHeight;
			stats.y =  stage.stageHeight - stats.height;
		}
	}
}